//===============================================================================
// Microsoft patterns & practices
// Mobile Client Software Factory - July 2006
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Practices.Mobile.ObjectBuilder;
using System.Reflection;
using System.Globalization;

namespace Microsoft.Practices.Mobile.ObjectBuilder
{
	/// <summary>
	/// Strongly-typed creation policy that allows clients to pass method handlers for the actual 
	/// creation and parameter retrieval for constructing a new instance of an object.
	/// </summary>
	/// <devdoc>
	/// ANY code that uses the mobile version of this class CANNOT be run on the desktop. See 
	/// http://lab.msdn.microsoft.com/ProductFeedback/viewFeedback.aspx?feedbackId=FDBK50657 and 
	/// GetMethodImplementationFlags commented-out method below.
	/// 
	/// IMPORTANT NOTE: this class CANNOT be used AS-IS on the desktop OB as it uses the FormatterServices to 
	/// create an uninitialized object (for circular dependencies purposes) and later on calls the constructor 
	/// as a method. We cannot support this usage using strong typing and delegates, as only new objects can 
	/// be constructed. Managed code (C# or VB) cannot express an invocation of a constructor without 
	/// actually creating a new object, which would not fit in the desktop OB approach to creation.
	/// </devdoc>
	public class StrongTypedCreationPolicy : ICreationPolicy
	{
		ConstructorInvokeHandler constructor;
		MethodParametersHandler getParameters;
		Type[] parameterTypes;

		/// <summary>
		/// Initializes the policy with the constructor method, parameter retrieval method and optional array of parameter types.
		/// </summary>
		/// <param name="constructor">Constructor method that will be called with the parameters retrieved by <paramref name="getParameters"/>, if any.</param>
		/// <param name="getParameters">Handler for retrieving the parameters to pass to the constructor handler. Can be <see langword="null" /> if no <paramref name="parameterTypes"/> are provided.</param>
		/// <param name="parameterTypes">Optional list of parameter types used by the constructor and parameter retrieval handlers.</param>
		public StrongTypedCreationPolicy(ConstructorInvokeHandler constructor, MethodParametersHandler getParameters, params Type[] parameterTypes)
		{
			Guard.ArgumentNotNull(constructor, "constructor");
			Guard.ArgumentNotNull(getParameters, "getParameters");
			
			this.constructor = constructor;
			this.getParameters = getParameters;
			this.parameterTypes = parameterTypes;
		}

		/// <summary>
		/// Implements <see cref="ICreationPolicy.SelectConstructor"/>, returning a custom <see cref="ConstructorInfo"/> that 
		/// will use the constructor handler implementation received in the constructor.
		/// </summary>
		public ConstructorInfo SelectConstructor(IBuilderContext context, Type type, string id)
		{
			return new StrongTypedConstructor(constructor, parameterTypes);
		}

		/// <summary>
		/// Implements the <see cref="ICreationPolicy.GetParameters"/> calling the parameter retrieval handler received 
		/// in the constructor.
		/// </summary>
		public object[] GetParameters(IBuilderContext context, Type type, string id, ConstructorInfo constructor)
		{
			return getParameters(context, id);
		}

		class StrongTypedConstructor : ConstructorInfo
		{
			ConstructorInvokeHandler constructor;
			Type[] parameterTypes;

			public StrongTypedConstructor(ConstructorInvokeHandler constructor, Type[] parameterTypes)
			{
				this.constructor = constructor;
				this.parameterTypes = parameterTypes;
			}

			public override object Invoke(object obj, BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
			{
				// This overload does not apply on OB Mobile as the constructor is NOT invoked 
				// as a method as in the Desktop OB which uses FormatterServices.GetSafeUninitializedObject.
				throw new NotImplementedException();
			}

			public override object Invoke(BindingFlags invokeAttr, Binder binder, object[] parameters, CultureInfo culture)
			{
				return constructor(parameters);
			}

			public override ParameterInfo[] GetParameters()
			{
				if (parameterTypes.Length == 0)
				{
					return new ParameterInfo[0];
				}

				ParameterInfo[] infos = new ParameterInfo[parameterTypes.Length];
				for (int i = 0; i < infos.Length; i++)
				{
					infos[i] = new StrongTypedParameter(parameterTypes[i]);
				}

				return infos;
			}

			#region NotSupported Members

#if !PocketPC
			
			// This override is required on the desktop CF, but it doesn't show up 
			// as an overridable member, so it doesn't compile. As a consequence, 
			// there's no way to run tests that use this class on the desktop.
			public override MethodImplAttributes GetMethodImplementationFlags()
			{
				throw new NotImplementedException();
			}
#endif

			public override MethodAttributes Attributes
			{
				get { throw new NotImplementedException(); }
			}

			public override RuntimeMethodHandle MethodHandle
			{
				get { throw new NotImplementedException(); }
			}

			public override Type DeclaringType
			{
				get { throw new NotImplementedException(); }
			}

			public override object[] GetCustomAttributes(Type attributeType, bool inherit)
			{
				throw new NotImplementedException();
			}

			public override object[] GetCustomAttributes(bool inherit)
			{
				throw new NotImplementedException();
			}

			public override bool IsDefined(Type attributeType, bool inherit)
			{
				throw new NotImplementedException();
			}

			public override string Name
			{
				get { throw new NotImplementedException(); }
			}

			public override Type ReflectedType
			{
				get { throw new NotImplementedException(); }
			}

			#endregion

			class StrongTypedParameter : ParameterInfo
			{
				public StrongTypedParameter(Type parameterType)
				{
					base.ClassImpl = parameterType;
				}
			}
		}
	}
}
